#-*- coding: utf-8 -*-

print("===============数据类型的底层实现===============")
#列表的底层实现
#引用数组的概念，列表内的元素可以分散的存储在内存中，列表存储的，实际上是这些元素的地址，这些地址的存储在内存中是连续的
list_1 = [1,[22,33,44],(5,6,7),{"name":"Sarah"}]
#浅拷贝只是将这些地址复制了一份，但是这些地址所指向的内容是相同的
list_2 = list(list_1) #浅拷贝 与list_1.copy()功能一样

#新增元素
list_1.append(100) #对list_1新存储一个地址，指向100
list_2.append("n")#对list_2新存储一个地址，指向n
print (list_1)
print (list_2)

#修改元素 将地址改为已经修改元素的地址
list_1[0] = 10
list_2[0] = 20
print("list_1: ",list_1)
print("list_2: ",list_2)
#对列表型元素进行操作，由于两个地址指向同一个列表行元素的地址，所以列表型元素的内容修改，两个列表内容都会修改
list_1[1].remove(44)
list_2[1] += [55,66]
print("list_1: ",list_1)
print("list_2: ",list_2)
#对元组型元素进行修改，注意元组是不可变的，对元组进行操作，会生成一个新的元组，原来的元组并没有改变
list_2[2] += (8,9)
print ("list _1:",list_1)
print ("list_2: ",list_2)
#对字典进行操作，原理和对列表进行操作一样，两个列表都会改变
list_1[-2]["age"] = 18
print("list_1: ",list_1)
print("list_2: ",list_2)

#引入深拷贝 深拷贝将所有层级的相关元素全部复制，完全分开，泾渭分明，避免了上述问题
import copy
list_1 = [1,[22,33,44],(5,6,7),{"name":"Sarah"}]
list_2 = copy.deepcopy(list_1)

list_1[-1]["age"] = 18
list_2[1].append(55)

print("list_1: ",list_1)
print("list_2: ",list_2)


#神秘的字典 字典的查找操作远远快于列表
#字典通过稀疏数组来实现值的存储和访问
#字典的创建过程 第一步：创建一个散列表（稀疏数组 N >>需要存储的值 n）
#第二步  根据计算的散列值确定其在散列表中的位置 极个别时候散列值会发生冲突，则内部有相应的解决冲突方法
#第三步 在该位置上存入值 访问的同理 而列表是从头开始顺序查找 所以速度比字典慢很多

#字符串的存储通过紧凑数组直接存储元素
#列表遍历需要注意的问题，注意用这种方式无法删除列表中所有的d，因为每一次删除之后，列表都会改变，但是for循环还是按照顺序循环的
#比如删除第一个d之后，新的列表就是["d","d","2","2","d","d","包"],但是for循环记得已经取过0个元素了，所以开始取第一个元素，于是少取了一个d
alist = ["d","d","d","2","2","d","d","包"]
for s in alist:
    if s == "d":
        alist.remove("d") #删除列表中第一次出现的该元素
print(alist)
#解决办法，使用负向索引,从-8访问到-1,先访问-8，再访问-7，以此类推，可以将所有d都删除掉
alist1 = ["d","d","d","2","2","d","d","包"]
for i in range(-len(alist),0):
    if alist[i] == "d":
        alist.remove(alist[i])
print(alist)
#多维列表的创建,注意乘以5是浅拷贝
ls = [[0]*10]*5
print (ls)
ls[0][0] = 1
print(ls)
#使用解析语法创建多维列表,在中括号里面，后面是对可迭代对象的遍历，每次取一个元素，执行相应的操作
#这个例子中，是每次取一个元素，就执行一次[0]*10 操作，创建一个[0]*10数组，最终生成一个5*10数组
ls = [[0]*10 for i in range(5)]
print (ls)
ls[0][0] =1
#因为各个数组是独立创建的，所有只修改其中一个
print(ls)

print("===================解析语法=================")
#解析语法的基本结构 以列表解析为例子（也称为列表推导）
#[expression   for value in iterable   if condition]
#三要数 ：表达式，可迭代对象，if条件
#执行过程：从可迭代对象中拿出一个元素，通过if条件（如果有的话），对元素进行筛选，若通过筛选，则把元素传递给表达式，若未通过，则进入下一次迭代
# 将表达式产生的结果作为列表的一个元素，直到迭代对象迭代结束，返回新创建的列表
#等价于如下代码
#result = []
#for value in iterabe:
#    if condition:
#        result.append(expression)
#例子 求20以内的奇数的平方
squares = []
for i in range(1,21):
    if i%2 == 1:
        squares.append(i**2)
print(squares)
print ([i**2  for i in range(1,21) if i%2 == 1])
#支持多变量
x = [1,2,3]
y = [1,2,3]
results = [i*j for i,j in zip(x,y)]
print (results)
#支持嵌套循环
colors = ["black","while"]
sizes = ["S","M","L"]
tshirts = ["{} {}".format(color,size) for color in colors for size in sizes]
print (tshirts)

#其它解析语法的例子
#解析语法构造字典（字典推导）
squares = {i:i**2 for i in range(10)}
for k,v in squares.items():
    print(k,": ",v)

#解析语法构造集合（集合推导）
squares = {i**2 for i in range(10)}
print(squares)
#生成器推导 可以进行迭代
squares = (i**2 for i in range(10))
for square in squares:
    print(square)

#条件表达式  expr1 if condition else expr2  如果条件成立，就执行expr1 ，否则执行expr2
#将n的绝对值赋值给x
n = -10
x = n if n>=0 else -n
print (x)

print("======================三大神器 生成器 迭代器 装饰器====================")
#生成器 如果要计算列表中1到100每个数的平方，采用列表迭代的方式，占用大量内存，此时可以使用生成器
#生成器采用惰性计算，海量数据，不需要存储 一边迭代一边计算 每次只算出当次需要的值，后面的值用到的时候再计算
#生成器实际上一直在执行next（）操作，取到下一个值，直到无值可取
#squares = (i**2 for i in range(10000))
#for i in squares
#    pass
#采用生成器无需显示存储全部数据，节省内存
print (sum(i for i in range(101)))
#生成器函数 yield
#以生成斐波那契数列为例子 数列的前两个元素为1,1,之后的元素为其前两个元素之和
def fib(max):
    ls = []
    n,a,b = 0,1,1
    while n < max:
        #ls.append(a)
        print (a) #每次打印输出斐波那契数列就可以了
        a,b = b,a+b
        n = n+1
    return ls

fib(10)

#使用yield，遇到yield语句，会将后面的元素返回，当它执行完返回后，就会停在这里，知道进行下一次next操作，它会从停止的地方继续执行
def fib(max):
    n,a,b = 0,1,1
    while n < max:
        yield a
        a,b = b,a+b
        n = n +1
fib(10)
for i in fib(10):
    print(i)

#迭代器
#可直接用于for循环的对象称为可迭代对象：iterable
#列表 元组 字符串 字典 集合 文件 都是可迭代对象 我们可以使用isinstance()判断一个对象是否是iterable对象
from collections import Iterable
print(isinstance([1,2,3],Iterable))
print(isinstance({"name":"Sarah"},Iterable))
print(isinstance('Python',Iterable))
#生成器也是可迭代对象
squares =  (i**2 for i in range(5))
print(isinstance(squares,Iterable))
#生成器不但可以用于for循环，还可以被next()函数调用
#每调用一次next函数，取出一个元素，直到没有数据可取，抛出StopIteration
print(next(squares))
print(next(squares))
#可以被next()函数调用并且不断返回下一个值，直到没有数据可取的对象称为迭代器： Iterator
#可以使用isinstance()判断一个对象是否是iterator对象
#生成器都是迭代器
from collections import Iterator
squares = (i**2 for i in range(5))
print(isinstance(squares,Iterator))
#列表 元组 字符串 字典 集合不是迭代器
print(isinstance([1,2,3],Iterator))
#可通过iter(iterable)创建迭代器
print(isinstance(iter([1,2,3]),Iterator))
#zip enumerate 等 itertools里的函数是迭代器
x = [1,2]
y = ["a","b"]
zip(x,y)
for i in zip(x,y):
    print(i)
print(isinstance(zip(x,y),Iterator))
#文件是迭代器
with open("test.txt","r",encoding="utf-8") as f:
    print(isinstance(f,Iterator))
#迭代器是可耗尽的
#range()不是迭代器,它不可以被next调用 且无法被耗尽，它是一种懒序列
numbers = range(10)
print(isinstance(numbers,Iterator))


#装饰器
#适用场景 需要对已经开发上线的程序添加某些功能，不能对程序中函数的源代码进行修改，不能改变程序中函数的调用方式
#函数对象 函数是python中的第一类对象 可以把函数赋值给变量 对该变量进行调用 可实现原函数的功能
def square(x):
    return x**2
print(type(square)) #square 是function类的一个实例
pow_2 = square  #可以理解为给这个函数起了一个别名pow_2
print(pow_2(5))
print(square(5))
#可以将函数作为参数传递
#高阶函数 接收函数作为参数，或者返回一个函数 满足这两个条件之一的函数称之为高阶函数
def square1(x):
    return x**2

def pow_2(fun): #高阶函数
    return fun

f = pow_2(square1) #将square1这个函数作为参数赋值给pow_2,它的返回值还是square1这个函数
print(f(8))
print(f==square1)

#嵌套函数 在函数的内部定义一个函数,并且调用
def outer():
    print("outer is running")
    def inner():
        print("inner is running")
    inner()

outer()

#闭包
#闭包是延伸了作用域的函数，如果一个函数定义在另一个函数作用域内，并且引用了外层函数的变量，则该函数称为闭包
#闭包是由函数及其相关的引用环境组合而成的实体（即 闭包=函数+引用环境），即不但返回内部函数，还返回外部环境的引用
def outer1():
    x = 1
    z = 10

    def inner():
        y = x + 100
        return y,z
    return inner

f = outer1() #f实际上包含了inner函数本身+outer函数的环境
print(f)
res = f()
print(res) #在inner函数中用到了outer函数的变量
#一旦在内层函数重新定义了相同名字的变量，则变量成为局部变量
def outer2():
    x = 1
    def inner():
        #x = x + 100 #这里x定义在内层函数中，默认为局部变量，内层找不到x的值，所以报错
        nonlocal x #用nonlocal声明x不是举报变量
        x = x + 10
        return x
    return inner

f = outer2()
f()

#一个简单的装饰器
#嵌套函数的实现
import time
#定义timer装饰器函数
def timer(func):
    #定义嵌套函数inner
    def inner():
        print("inner run")
        start = time.time()
        #在inner中运行传递过来的函数，并且在前后用两个时间戳获得它运行的时间
        func()
        end = time.time()
        print("{}函数运行用时{:.2f}秒".format(func.__name__,(end-start)))
    return inner()

#定义一个f1函数，我们希望它在运行的时候，统计一下用了多长时间
def f1():
    print("f1 run")
    time.sleep(1)

#将f1函数传递到timer()函数里面去，返回值是inner，此时返回值f1不单单包括inner函数，还包括timer函数的外部环境，包括参数func
f1 = timer(f1)
f1

#语法糖
@timer    #相当于实现了f2 = timer(f2)
def f2():
    print("f1 run")
    time.sleep(1)
f2

#装饰有参函数
def timer1(func):
    #定义嵌套函数inner
    def inner(*args, **kwargs):
        print("inner run")
        start = time.time()
        #在inner中运行传递过来的函数，并且在前后用两个时间戳获得它运行的时间
        func(*args,**kwargs)
        end = time.time()
        print("{}函数运行用时{:.2f}秒".format(func.__name__,(end-start)))
    return inner()

@timer1
def f3(n1,n2):
    print("f3 run")
    time.sleep(n)
#f3(3) 报错？


#装饰器本身也要传递一些额外的参数 理解闭包是关键

#何时执行装饰器 一装饰就执行 不必等调用
#通过python内部装饰器 @wraps可以将掩饰掉的原函数的属性重新回来



























































































































































